#ifndef NJF_RASTER_CHANNELS_H
#define NJF_RASTER_CHANNELS_H

ALWAYS_INLINE int IsPixelOpaque(Pixel p)
{
    return ((p & PIXEL_TARGET_ALPHA_MASK) == PIXEL_TARGET_ALPHA_MASK);
}

ALWAYS_INLINE void GetColorChannels(unsigned int *cc, Pixel pixel)
{
    cc[0] = pixel & 0xFF;
    cc[1] = (pixel >> 8) & 0xFF;
    cc[2] = (pixel >> 16) & 0xFF;
    cc[3] = (pixel >> 24) & 0xFF;
}
ALWAYS_INLINE Pixel PixelFromColorChannels(unsigned int *cc)
{
    return (cc[0] | (cc[1] << 8) | (cc[2] << 16) | (cc[3] << 24));
}
ALWAYS_INLINE Pixel AlphaBlendPixels(Pixel p1, Pixel p2)
{
    /*
        This warrants explanation.
        Basically, we're just cutting the required multiplications in half
            using faster bit shifts and masking.
        This requires a bit of basic math knowledge to understand:
            p2(r, b) = ((a2 * r) * x) + (a2 * b)
                     = (a2 * (r * x)) + (a2 * b)
                     = a2 * ((r * x) + b)
            p2(a, g) = (a2 * y) + ((a2 * g) * z)
                     = (a2 * y) + (a2 * (g * z))
                     = a2 * (y + (g * z))
            p1(r, b) = (((255 - a2) * r) * x) + ((255 - a2) * b)
                     = ((255 - a2) * (r * x)) + ((255 - a2) * b)
                     = (255 - a2) * ((r * x) + b)
            p2(a, g) = (((255 - a2) * a1) * y) + (((255 - a2) * g) * z)
                     = ((255 - a2) * (a1 * y)) + ((255 - a2) * (g * z))
                     = (255 - a2) * ((a1 * y) + (g * z))
            result = p1 + p2

            we can't compute them all together because the multiplication
                produces worthless bits that need to be masked out
                and it would overflow uint32_t

            this method saves   6 right shifts (unpacking),
                                3 right shifts (repacking),
                                2 ands (-8 unpacking, +6 masking),
                                2 ors (-4 packing, +1 p2 alpha, +1 final packing)
                                2 multiplications (-2 arithmetic)
                                2 additions (-2 arithmetic)
            over the previous method
    */
    static const int AMASK = 0xFF000000;
    static const int RBMASK = 0x00FF00FF;
    static const int GMASK = 0x0000FF00;
    static const int AGMASK = 0xFF00FF00;
    static const int ONEALPHA = 0x01000000;
    unsigned int a = ((p2 & AMASK) >> 24);
    unsigned int na = 255 - a;
    /* compute red and blue result together
        >>8 to divide by 256
    */
    unsigned int rb = ((na * (p1 & RBMASK)) + (a * (p2 & RBMASK))) >> 8;
    /* compute alpha and green together
        we have to shift right by 8 to prevent overflow, so we don't need to right-shift again
    */
    unsigned int ag = (na * ((p1 & AGMASK) >> 8)) + (a * (ONEALPHA | ((p2 & GMASK) >> 8)));
    /*
        mask out what would be the low-order bits of the individual multiplications
        and return our result
    */
    return (Pixel)((rb & RBMASK) | (ag & AGMASK));
}

ALWAYS_INLINE void ModulateChannel(unsigned int* ch, unsigned int wht, unsigned int blk)
{
    if(wht > blk)
       *ch = blk + ((*ch * (wht - blk)) / 255);
    else
       *ch = wht + (((255 - *ch) * (blk - wht)) / 255);
}

ALWAYS_INLINE Pixel ModulateColors(unsigned int *pcc, unsigned int *wcc, unsigned int *bcc)
{
    ModulateChannel(&pcc[0], wcc[0], bcc[0]);
    ModulateChannel(&pcc[1], wcc[1], bcc[1]);
    ModulateChannel(&pcc[2], wcc[2], bcc[2]);
    ModulateChannel(&pcc[3], wcc[3], bcc[3]);
    return PixelFromColorChannels(pcc);
}

/* for custom blitters */
ALWAYS_INLINE Pixel AdaptPixel(Pixel input, BlitMode *mode)
{
    Pixel ret = input;
    if(mode->modes & BLIT_COLORMOD)
    {
        unsigned int wcc[4];
        unsigned int bcc[4];
        unsigned int pcc[4];
        GetColorChannels(wcc, mode->mod.color.wht);
        GetColorChannels(bcc, mode->mod.color.blk);
        GetColorChannels(pcc, ret);
        ret = ModulateColors(pcc, wcc, bcc);
    }
    if(mode->modes & BLIT_ALPHAMOD)
    {
        unsigned int newalpha = (ret >> 24) & 0xFF;
        ModulateChannel(&newalpha, mode->mod.alpha.high, mode->mod.alpha.low);
        ret = ((ret) & 0x00FFFFFF) | (newalpha << 24);
    }
    if(mode->modes & BLIT_OPAQUE)
    {
        ret |= PIXEL_TARGET_ALPHA_MASK;
    }
    return ret;
}

#endif /* NJF_RASTER_CHANNELS_H */
